package com.well.register.console.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.well.register.console.aop.WellException;
import com.well.register.console.dao.JobMapper;
import com.well.register.console.domain.Job;
import com.well.register.console.model.job.JobStatus;
import com.well.register.console.model.job.QueryJobsParam;
import com.well.register.console.service.JobExecutorService;
import com.well.register.console.service.JobService;
import com.well.register.console.service.ServerService;
import com.well.register.event.job.RegisterJobEvent;
import com.well.register.event.ServerHandler;
import com.well.register.event.job.WellJobRunner;
import com.well.register.util.ThreadUtils;
import com.well.rpc.model.JobHandlerModel;
import com.well.rpc.model.RegisterModel;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;

import static com.well.register.console.model.job.ErrorInfo.*;

/**
 * <p>
 * job service impl
 * </p>
 *
 * @author weedo
 * @since 2022/8/7
 */

@Service
public class JobServiceImpl extends ServiceImpl<JobMapper, Job> implements JobService {

    @Resource
    private RegisterJobEvent registerJobEvent;

    @Resource
    private ServerService serverService;
    @Resource
    private JobExecutorService jobExecutorService;

    @Override
    public void saveJob(JobHandlerModel model, RegisterModel registerModel) {
        String serverInfo = getServerInfo(registerModel);
        Job job = getJob(model.getJobName(), registerModel);
        if (job == null) {
            job = new Job();
            Integer defaultKey = 0;
            job.setThreadKey(defaultKey);
            job.setCreateTime(new Date());
            job.setJobName(model.getJobName());
            job.setServerInfo(serverInfo);
        }
        job.setStatus(JobStatus.RUNNING.getCode());
        job.setCronExpression(model.getCron());
        job.setUpdateTime(new Date());
        this.saveOrUpdate(job);
    }

    @Override
    public Job getJob(String  jobName, RegisterModel registerModel) {
        String serverInfo = getServerInfo(registerModel);
        QueryWrapper<Job> wrapper = new QueryWrapper<>();
        wrapper.eq("job_name", jobName);
        wrapper.eq("server_info", serverInfo);
        return this.getOne(wrapper);
    }

    private String getServerInfo(RegisterModel registerModel) {
        return registerModel.getNameSpace() + ":" +
                registerModel.getGroup() + ":" +
                registerModel.getServerName();
    }

    @Override
    public List<Job> getJobs(RegisterModel registerModel) {
        String serverInfo = getServerInfo(registerModel);
        QueryWrapper<Job> wrapper = new QueryWrapper<>();
        wrapper.eq("server_info", serverInfo);
        return this.list(wrapper);
    }

    @Override
    public List<Job> selectJobLists(QueryJobsParam param) {
        QueryWrapper<Job> wrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(param.getJobName()))
            wrapper.eq("job_name", param.getJobName());
        if (null != param.getStatus()) {
            wrapper.eq("status", param.getStatus());
        }
        return this.list(wrapper);
    }

    @Override
    public boolean stopJobById(Integer id) {
        Job job = this.getById(id);
        if (job.getStatus() != JobStatus.RUNNING.getCode()) {
            throw new WellException(JOB_STATUS_ERROR.getCode(), JOB_STATUS_ERROR.getMsg());
        }
        ServerHandler.cancelScheduledFuture(job.getServerInfo() + ":" + job.getJobName());
        // 需要考虑集群下任务取消同步
        job.setStatus(JobStatus.STOP.getCode());
        job.setUpdateTime(new Date());
        return this.updateById(job);
    }

    @Override
    public boolean restartJobById(Integer id) {
        Job job = this.getById(id);
        if (job == null) {
            throw new WellException(INVALID_ID.getCode(), INVALID_ID.getMsg());
        }
        if (job.getStatus() == JobStatus.RUNNING.getCode()) {
            throw new WellException(JOB_STATUS_ERROR.getCode(), JOB_STATUS_ERROR.getMsg());
        }
        RegisterModel model = new RegisterModel();
        String[] split = job.getServerInfo().split(":");
        if (split.length != 3) {
            throw new WellException(SERVER_INFO_ERROR.getCode(), SERVER_INFO_ERROR.getMsg());
        }
        if (!serverService.haveValidServer(split[0], split[1], split[2])) {
            throw new WellException(SERVER_STATUS_ERROR.getCode(), SERVER_STATUS_ERROR.getMsg());
        }
        model.setNameSpace(split[0]);
        model.setGroup(split[1]);
        model.setServerName(split[2]);

        JobHandlerModel jobHandlerModel = new JobHandlerModel();
        jobHandlerModel.setCron(job.getCronExpression());
        jobHandlerModel.setJobName(job.getJobName());
        registerJobEvent.registerJob(model, jobHandlerModel);
        return true;
    }

    @Override
    public boolean startJobNow(Integer id) {
        Job job = this.getById(id);
        if (job == null) {
            throw new WellException(INVALID_ID.getCode(), INVALID_ID.getMsg());
        }
        RegisterModel model = new RegisterModel();
        String[] split = job.getServerInfo().split(":");
        if (split.length != 3) {
            throw new WellException(SERVER_INFO_ERROR.getCode(), SERVER_INFO_ERROR.getMsg());
        }
        if (!serverService.haveValidServer(split[0], split[1], split[2])) {
            throw new WellException(SERVER_STATUS_ERROR.getCode(), SERVER_STATUS_ERROR.getMsg());
        }
        model.setNameSpace(split[0]);
        model.setGroup(split[1]);
        model.setServerName(split[2]);
        ThreadUtils.execute(new WellJobRunner(model, job.getJobName(), 0, jobExecutorService));
        return true;
    }
}
